iT邦幫忙

2021 iThome 鐵人賽

DAY 16
3

你設計的 API 除了跑的動以外,它安全穩定嗎?

既然 Junoir 跟 Senior 的後端工程師都能寫出可以運作的 API;那公司為什麼要多花錢請一個比較貴的工程師呢?

不僅面試官會問這個問題,你也要時常反問自己:「我憑什麼可以開出更高的薪水?我跟其他的工程師有哪裡不一樣?

大綱

  1. 設計 API 時會考慮哪些點?

    • 1.1 面試官為什麼會問?
    • 1.2 面試官想從答案確認什麼?
    • 1.3 筆者提供的簡答
  2. 回答問題所需具備的知識

    • 2.1 驗證 Client 端身份
    • 2.2 了解後端驗證的重要性,並認識常見的 Validation Rule
    • 2.3 優化後端的常見方法
    • 2.4 淺談 Unit Test(單元測試)
  3. 衍伸問題

    • 3.1 你知道 PUT & PATCH 的差異嗎?
    • 3.2 請舉例 RESTful api 的命名原則
    • 3.3 GraphQL 跟 RESTful API 的差異在哪裡?

1. 設計 API 時會考慮哪些點?

1.1 面試官為什麼會問?

能依照需求規格書寫好 API 只是對後端工程師的基礎要求;但這個 API 考慮的是否周全就看每個工程師的經驗了。

這算是常見面試題,面試官想從回答中了解你後端的實作經驗與基本功,並判斷你在進入團隊後是否需要花更多時間訓練才能成為有效戰力。


1.2 面試官想從答案確認什麼?

  • 你設計的 API 是否有防護的機制
  • 當 Client 端傳入不符規範的參數及型態時,你的 API 是否有成為防線
  • 你有用什麼方法保證 API 的穩定性
  • 是否有思考過 API 的優化

1.3 筆者提供的簡答

以過去設計的內部管理系統來說,Client 端在登入後會取得 JWT,之後在存取 API 前會用這個 JWT 來驗證是否有存取的權限;為了避免 Client 端使用不符規範的參數,我會先驗證每個傳入參數的格式,並撰寫 Unit Test 確保這隻 API 在不同情境下的 Response 都符合期待;再確認 API 的穩定性後,我會去思考還有什麼地方可以優化,讓 Client 端有更好的體驗。


2. 回答問題所需具備的知識

2.1 驗證 Client 端身份

這邊帶讀者瞭解兩種主流驗證機制,大部分的框架都有提供對應的方案供開發人員使用。

  • Session 與 Cookie
    當 Client 端登入後,Server 會產生一筆 Session 並記錄(可能存在資料庫或記憶體),同時將可以識別身份的 SessionID 送回 Client 端儲存於 Cookie;此後 Client 端的請求,Server 只需要根據 Cookie 中的 SessionID 尋找 Session 來驗證使用者即可。
    • 使用 Session 要考量的問題
      1. 因為 Session 的資料存在 Server 中,需考慮同時有很多使用者上線時要面對的效能或是記憶體問題
        • 解決方案:定期清理過期 Session。
      2. 如果採取 Cluster 部署,就要考慮不同 Server 的 Session 共享問題
        • 解決方案:Server 需設計 Session 同步機制(Replication)。
  • JSON Web Token(JWT)
    當 Client 端登入成功後,Server 會回傳一個 JWT,Client 端通常會將 JWT 儲存於 localstorage;當 Client 端打算訪問受保護的資源時,需要在 Header 的 Authorization 使用 Bearer 模式添加 JWT,如果驗證通過即可存取相關資源。
    • 使用 JWT 要考量的問題
      1. 在 Client 端取得 JWT 後,在 JWT 有效期內他都能夠使用;即使我們直接把使用者帳號刪除,這個 JWT 依舊可以訪問受保護的資源。
        • 解決方案:設計黑名單機制,使用記憶體資料庫維護一份黑名單,如果想讓某個 JWT 失效,把它加入黑名單就好了。
      2. 不要為了方便把 JWT 的有效期限設定太長,不然這個 JWT 洩露出去後,任何人都可以拿它來幹大事。
        • 解決方案:JWT 有效期限不要太長,同時使用 HTTPS 來減少盜用。

2.2 了解後端驗證的重要性,並認識常見的 Validation Rule

無論前端驗證做得多完美,後端驗證都是必須存在的;因為想攻擊系統的人不一定會從前端執行,直接 Call API 嘗試所有可能性的效率快多了;前端驗證可以大幅減少不必要的 Request 產生,後端驗證則是保護資料的最終防線

  • 為什麼要了解 Validation Rule?

    • 你可以節省非常多親手撰寫 Function 的時間(自己寫的效能跟穩定度未必好,還很佔版面)。
    • 這些不符規範的資料並不會進入後端運算的邏輯層面。
    • 驗證發現錯誤就 Response Error,減少 Client 端等待時間。

接下來用情境來介紹幾個常見的 Validation Rule,不同框架的名字可能略有不同,我這邊以 Laravel 的為主。

  • 驗證使用者註冊系統時填寫的參數

    • 此為 Laravel 驗證傳入參數的程式,下方依據參數進行解說。

      $validator = Validator::make($request->all(), [
        'email' =>  ['required', 'email', 'string', 'max:255', 'unique:users'],
        'name' =>  ['required', 'string', 'max:50'],
        'password' => ['required', 'string', 'max:255', 'min:8', 'regex:/^.*(?=.{8,})(?=.*?[a-zA-Z])(?=.*?[0-9]).*$/', 'confirmed']
      ]);
      
      • email
        為必填 required 欄位,同時檢查傳入的字串是否符合 email 格式以及長度 max 是否超過限制,並確認這個 email 並沒有被註冊過 unique
      • name
        為必填 required 欄位,同時檢查傳入的字串長度 max 是否超過限制。
      • password
        為必填 required 欄位,除了檢查傳入的字串長度 max 是否超過限制外,通常會限制最小長度 min;再嚴謹一點會要求 password 複雜度需包含英數字 regex,且內容與 password_confirmation 欄位相同 confirmed

2.3 優化後端的常見方法

這邊先不討論程式語言和資料庫選擇帶來的影響,專注在共同可行性方案上。

  • 操作資料庫時加入限制條件
    就算前端 Request 的參數沒有限制條件(ex:分頁、筆數、種類...),後端也要有意識的設定預設值,不然當資料量大的時候除了會跑很久浪費效能外,前端也不適合接收/呈現這麼多資訊。

  • 回傳前端的資料盡量是已經計算過的
    Client 端使用的機器效能未知,如果把太多的邏輯運算交給前端可能導致不好的使用者體驗;我這邊建議後端最好回傳計算後的資料,前端負責顯示為主,不過這塊也要看實際的業務量以及情境來 trade-off 。

  • 加入 CDN Cache
    如果遇到國外客戶抱怨連線速度很慢,就可以考慮採用 CDN 來解決這個問題;CDN 服務商的做法是將 Server 建置在世界各地,將網站上的靜態內容(圖片、影音、檔案)與動態內容(資料庫查詢)Cache 到 Server,根據訪客的地理位置,從最近的 Server 提供資料;好處如下:

    • 提升網站的穩定度(如果某個 CDN Server 掛了也有替身)。
    • 節省原本 Server 的頻寬(因為流量被分散了)。
    • 改善使用者體驗(過去會卡住的都是大檔案,有了 CDN Cache 後瀏覽會更流暢)。
  • 提升硬體水平
    江湖傳言:「效能不夠,機器來湊!」雖然聽起來好像是在開玩笑;但實際業務上如果真的遇到效能瓶頸,許多公司的做法就是先靠硬體讓服務穩定;服務穩定了,你才有心力去 Refactor 程式。


2.4 淺談 Unit Test(單元測試)

  • 為什麼 Unit Test 很重要?
    • 有它保證程式碼的穩定性,發生錯誤時可以快速找到源頭,因此開發人員可以更大膽的開發。
    • 減少手動測試的時間,大幅降低相關維運成本
    • API 版本更新時(ex:v2 升級到 v3),確保舊版 API 可以正常運行
  • Unit Test 使用時機
    • 開發人員要先在本機 Run 過一遍,確認所有 Unit Test 都通過。
    • 如果有使用 Git 做版本控制,在特定 branch 合併時 CI/CD 應該要有某個 Stage 來做測試,確保合併分支的程式正確性
  • 撰寫 Unit Test 基礎概念
    • 測試時使用(或自動建立)Mock data,不要動到 Server 資料
      Unit Test 著重在測試程式邏輯,建議測試時使用 Mock data,不然可能導致正式環境發生意外。
    • 測試要涵蓋你程式中的所有邏輯
      • 假設一個 API 有 12 種回傳邏輯,你的 Unit Test 就要有 12 個;根據莫非定律,少寫的測試往往上線後就會爆掉
      • 如果 API 有做 JWT 防護、Request 參數驗證,我建議要把所有錯誤情境都做一次模擬;甚至要模擬 JWT 還在有效期,但實際權限已被移除的邊緣測試

筆者聽過有些公司在專案正式 Release 時沒有撰寫任何的 Unit Test,直接讓客戶人肉測試,等客戶回報錯誤時再去修復 Bug;嗯...這種公司的離職率通常很高,因為有一堆歷史業障需要修但沒人敢碰。


3. 衍伸問題

3.1 你知道 PUT & PATCH 的差異嗎?

考點:對於 API 基礎操作的掌握程度

「PUT」是替換資源,而「PATCH」則是更新部份資源內容;以使用者舉例,假設他有「姓名、簡介、年齡」這幾個欄位,如果想更新「姓名」的資訊:

  • 用 PUT 方法送出更新,除了「姓名」外還要填寫所有的欄位,如果沒填寫會導致其他的欄位更新成預設值。
  • 用 PATCH 方法送出更新,只需填寫要更新的「姓名」欄位即可。

3.2 請舉例 RESTful API 的命名原則

考點:確認你寫 API 的習慣是否符合標準

假設今天有一個 users 的 Table。

  • 取得 user list
    • 方法:GET
    • API 路徑/api/users
  • 取得 user detail
    • 方法:GET
    • API 路徑/api/users/{user_id}
  • 新增 user
    • 方法:POST
    • API 路徑/api/users
  • 更新 user 資訊
    • 方法:PUT
    • API 路徑/api/users/{user_id}
  • 刪除 user
    • 方法:DELETE
    • API 路徑/api/users/{user_id}

3.3 GraphQL 跟 RESTful API 的差異在哪裡?

考點:確認你對市場技術的認知程度

過去 RESTful API 需要根據需求規格書給每個 API 設計回傳的資料結構,如果這個 API 在很多頁面被共用就有可能要回傳更多的資訊,但回傳的資訊中有些是只有特定頁面才有需求的,假使拆成多隻 API 也會導致日後維護困難;而 GraphQL 的出現正好解決了這個問題,他能夠由 Client 端來定義 Server 端要回傳的資料結構,不再回傳冗贅資訊。

  • 補充:用範例了解兩者差異
    要查詢圖書館中特定書籍,回傳內容須包含:「作者、書名、出版年」。

    • GraphQL

      • Client 端傳送的 query

        query {
          books (id:12) {
            authors {
                firstName
                lastName
            }
            title
            yearPublished
          }
        }
        
      • Server 端 Response 的 result

        {
          "data": {
            "books": {
              "authors": [
                {
                  "firstName": "Lin",
                  "lastName": "Dean"
                }
              ],
              "title": "全端工程師生存筆記",
              "yearPublished": "2022"
            }
          }
        }
        
    • RESTful API

      • Client 端的 Request
        需要拆成GET api/books/12GET api/authors/12來分別取得書籍、作者資訊。

      • Server 端 Response 的 result
        GET api/books/12

        {
          "title" : "全端工程師生存筆記",
          "authorID": 12,
          "yearPublished" : 2022,
          "page": 168,
          "tags": ["履歷"、"面試"、"職場"]
        }
        

        GET api/authors/12

        {
          "firstName": "Lin",
          "lastName": "Dean"
        }
        

    在上面的範例中,RESTful API 需要兩個 Request 才能取得完整資料,如果想用一個 Request 取得完整資料就要重新建立一支 API;比較起來GraphQL 對前端工程師來說是更好使用的工具;不過轉換與建立的成本較 RESTful API 高,如果想嘗試 GraphQL 帶來的效益,可以考慮漸進式的遷移。


感謝大家的閱讀,如果喜歡我的文章可以訂閱接收通知;如果有幫助到你,按Like可以讓我更有寫文的動力,我們明天見~

參考資源

  1. 進階 RESTful API 討論
  2. 透過 JWT 實作驗證機制
  3. 認識 Cookie、Session、Token 與 JWT
  4. 簡單聊一聊 Cookie、Session、Token、JWT 的區別和作用

我在 Medium 平台 也分享了許多技術文章
❝ 主題涵蓋「MIS & DEVOPS資料庫前端後端MICROSFT 365GOOGLE 雲端應用自我修煉」希望可以幫助遇到相同問題、想自我成長的人。❞


https://ithelp.ithome.com.tw/upload/images/20230512/20103256twZPv1G4XH.jpg

在許多人的幫助下,本系列文章已成功出版,除了添加新的篇章,更完善了每個案例的應對進退;如果對現在的職涯感到迷茫,也許這本書能帶給你不一樣的觀點~

天瓏書局: https://www.tenlong.com.tw/products/9786263334571


上一篇
[面試][後端]在正式 API 完成前,如何讓要串接的工程師不要空等?
下一篇
[面試][資料庫]設計資料庫時會考量哪些點?
系列文
全端工程師生存筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言